This is my Advanced genomics’ project for the AY 2021/2022. Data and code can be retrieved from the following GitHub link.
In protein-protein interaction (PPI from now on) networks (therefore proteins that interact with many other proteins) we distinguish hubs (highly connected nodes/proteins) into party and date categories. This differentiation comes from an analysis (Han et al.) where it was shown that hubs of the PPI network can be assigned to two different categories:
· Hubs that interact with many partners at the same moment (party hubs), which also connect proteins within functional modules such as protein complexes. Party hubs components’ expression is correlated with its interaction partners.
· Hubs that have many targets, but interact with only one or a few of them at any moment (date hubs). In contrast to party hubs, date hubs do not exhibit such a correlation and appear to connect different functional modules.
A popular PPI networks functional enrichment analysis tool is STRING.
The validity of the date hub/party hub distinction was disputed, the concept might not be as general as it seemed when the original publication was presented. In particular, in this paper, the authors showed that the reported importance of date hubs to network connectivity can in fact be attributed to a tiny subset of them.
The date/party distinction was originally motivated by an approximately bimodal distribution of hub co-expression, but this feature is not always robust to methodological changes. Additionally, topological properties of hubs do not in general correlate with co-expression. According to the paper, thinking in terms of a date/party dichotomy for hubs in protein interaction networks is not meaningful, and it might be more useful to conceive of roles for protein-protein interactions rather than for individual proteins.
This project is based on the fact that the party and date hub concept may be interesting to be merged with another topological property of nodes, betweenness centrality, that we can use to introduce the bottleneck concept into the party/date hubs conceptualization. Betweenness (i.e., “bottleneck-ness”) is a much more significant indicator of essentiality than degree (i.e., “hub-ness”). Furthermore, bottlenecks correspond to the dynamic components of the interaction network—they are significantly less well coexpressed with their neighbors than non-bottlenecks, implying that expression dynamics is wired into the network topology.
Therefore, combining hubs and bottlenecks, we can define for instance the following categories of proteins, defined on topological properties: non-bottleneck hubs (NBH), bottleneck hubs (BH), bottleneck non-hubs (BNH) and non-bottleneck non-hubs (NBNH, the largest group by definition).

This project aims at integrating the 4 categories above with the idea of party and date hubs using an interactome for E. coli and a gene expression compendium (since the interaction among two proteins can only take place if the two proteins are expressed at the same moment, one can use gene expression compendia to assign the hubs to the party or date class). The network is used to define the above groups based on the two topological properties (degree, to define hubness, and betweenness centrality, to define bottleneckness).
Let’s start reading E. Coli protein physical links txt file…
rm(list=ls()) # Just good practice to ensure we'll work on a "clean" environment
ECOPPITABLE <- read.table("https://github.com/emanuelecavalleri/partyANDdatePPnetworks/blob/main/511145.protein.physical.links.full.v11.0.txt?raw=true", header=TRUE)
and have a look at it.
head(ECOPPITABLE)
ECOPPITABLE contains experimentally determined interactions. The experiments column indicates some sort of “confidence” in the experiment. We want to have a filter at some value to increase the confidence, and to accomplish that we first take a look at the “trend” of confidence values.
# ECOPPITABLE's dimensions
dim(ECOPPITABLE)
[1] 368774 10
# Confidence values > 0:
length(ECOPPITABLE$experiments[(which(ECOPPITABLE$experiments > 0))])
[1] 6060
# Value of the min confidence value > 0:
min(ECOPPITABLE$experiments[(which(ECOPPITABLE$experiments > 0))])
[1] 46
# Number of values above certain threshold (useful to extract values' "trend")
c(
length(ECOPPITABLE$experiments[(which(ECOPPITABLE$experiments > 46))]),
length(ECOPPITABLE$experiments[(which(ECOPPITABLE$experiments > 100))]),
length(ECOPPITABLE$experiments[(which(ECOPPITABLE$experiments > 200))]),
length(ECOPPITABLE$experiments[(which(ECOPPITABLE$experiments > 300))]),
length(ECOPPITABLE$experiments[(which(ECOPPITABLE$experiments > 400))]),
length(ECOPPITABLE$experiments[(which(ECOPPITABLE$experiments > 500))]),
length(ECOPPITABLE$experiments[(which(ECOPPITABLE$experiments > 600))]),
length(ECOPPITABLE$experiments[(which(ECOPPITABLE$experiments > 700))]),
length(ECOPPITABLE$experiments[(which(ECOPPITABLE$experiments > 800))])
)
[1] 6036 4650 2554 1778 644 628 628 628 456
# Max confidence value
max(ECOPPITABLE$experiments)
[1] 896
At a first glimpse, by looking at the number of entries for each subset, we can see number of items reaching a plateau around values > 400, in particular:
# Value of the min confidence value > 400:
min(ECOPPITABLE$experiments[(which(ECOPPITABLE$experiments > 400))])
[1] 406
# Value of the min confidence value > 406:
min(ECOPPITABLE$experiments[(which(ECOPPITABLE$experiments > 406))])
[1] 762
Great gap!
# Number of values above 406 (values ≥ 762) threshold
length(ECOPPITABLE$experiments[(which(ECOPPITABLE$experiments > 406))])
[1] 628
The plateau starts at confidence value = 762; this “empirical” stuff helped me retain an appropriate threshold might be 406/762. But let’s go into further details and make a histogram and a barplot, after the removal of confidence values = 0.
minExp <- 0
# Remove confidence values = 0 while keeping only columns of interest for this project
ECOEXP <- as.data.frame(cbind(ECOPPITABLE$protein1[ECOPPITABLE$experiments>minExp], ECOPPITABLE$protein2[ECOPPITABLE$experiments>minExp], ECOPPITABLE$experiments[ECOPPITABLE$experiments>minExp]))
colnames(ECOEXP) <- c("p1", "p2", "weight")
ECOEXP$weight <- as.numeric(ECOEXP$weight)
barplot(ECOEXP$weight, ylab="Confidence", ylim=c(0,1000))

hist(ECOEXP$weight, xlab="Confidence", xlim=c(0,1000), ylim=c(0,2000), main="Histogram of confidence values", col = c(rep("gray", 9), rep("red", 9)))
legend("topright", c("Confidence values ≤ 406", "Confidence values > 406"), col=c("gray", "red"), lwd=10)

From the barplot we can see that above 400 we have less “density” (due to the 406-762 “gap” we talked before), and from the histogram we confirm our empirical plateau hypothesis. Being said that, we continue our analysis with a > 406 (or, equally, ≥ 762) confidence threshold (may be too high/have too little interactions? Results with a lower threshold showed similar results, but with a higher computational complexity).
minExp <- 406
# Remove confidence values > 406 while keeping only columns of interest for this project
ECOEXP <- as.data.frame(cbind(ECOPPITABLE$protein1[ECOPPITABLE$experiments>minExp], ECOPPITABLE$protein2[ECOPPITABLE$experiments>minExp], ECOPPITABLE$experiments[ECOPPITABLE$experiments>minExp]))
colnames(ECOEXP) <- c("p1", "p2", "weight")
ECOEXP$weight <- as.numeric(ECOEXP$weight)
Below, we also define a much larger network obtained by transferring experimental information from other species (experiments_transferred column involved here). As above, we filter it to retain values for which confidence is relatively high, so let’s have a look at the trend.
minExp <- 0
# Remove confidence values = 0 while keeping only columns of interest for this project
ECOEXP_TRANSF <- as.data.frame(cbind(ECOPPITABLE$protein1[ECOPPITABLE$experiments_transferred>minExp], ECOPPITABLE$protein2[ECOPPITABLE$experiments_transferred>minExp], ECOPPITABLE$experiments_transferred[ECOPPITABLE$experiments_transferred>minExp]))
colnames(ECOEXP_TRANSF) <- c("p1", "p2", "weight")
ECOEXP_TRANSF$weight <- as.numeric(ECOEXP_TRANSF$weight)
# Confidence values > 0:
length(ECOEXP_TRANSF$weight[(which(ECOEXP_TRANSF$weight > 0))])
[1] 125894
# Value of the min confidence value > 0:
min(ECOEXP_TRANSF$weight[(which(ECOEXP_TRANSF$weight > 0))])
[1] 45
# Number of values above certain threshold (useful to extract value's "trend")
c(
length(ECOEXP_TRANSF$weight[(which(ECOEXP_TRANSF$weight > 45))]),
length(ECOEXP_TRANSF$weight[(which(ECOEXP_TRANSF$weight > 100))]),
length(ECOEXP_TRANSF$weight[(which(ECOEXP_TRANSF$weight > 200))]),
length(ECOEXP_TRANSF$weight[(which(ECOEXP_TRANSF$weight > 300))]),
length(ECOEXP_TRANSF$weight[(which(ECOEXP_TRANSF$weight > 400))]),
length(ECOEXP_TRANSF$weight[(which(ECOEXP_TRANSF$weight > 500))]),
length(ECOEXP_TRANSF$weight[(which(ECOEXP_TRANSF$weight > 600))]),
length(ECOEXP_TRANSF$weight[(which(ECOEXP_TRANSF$weight > 700))]),
length(ECOEXP_TRANSF$weight[(which(ECOEXP_TRANSF$weight > 800))]),
length(ECOEXP_TRANSF$weight[(which(ECOEXP_TRANSF$weight > 900))])
)
[1] 125890 92036 16020 9294 6122 5182 4930 4742 4142 3138
# Max confidence value
max(ECOEXP_TRANSF$weight)
[1] 999
And then, as previously done, we make a barplot and a histogram to help us.
barplot(ECOEXP_TRANSF$weight, ylab="Confidence", ylim=c(0,1000))

hist(ECOEXP_TRANSF$weight, xlab="Confidence", xlim=c(0,1000), ylim=c(0,60000), main="Histogram of confidence values", col = c(rep("gray", 9), rep("red", 11)))

Which is the value for an appropriate threshold?
tail(table(ECOEXP_TRANSF$weight), n=300)
396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419
48 24 8 12 32 20 18 18 36 16 20 34 8 2 2 2 2 26 20 8 4 10 6 48
420 421 423 424 425 427 428 429 430 431 432 433 434 435 443 448 449 450 451 455 456 457 459 465
6 4 2 6 6 62 16 12 18 204 24 38 10 10 82 38 2 4 2 2 2 40 2 4
466 468 469 470 473 474 477 481 504 506 507 508 509 510 511 513 520 524 525 532 534 537 538 543
4 2 26 2 2 2 4 2 2 4 4 32 10 2 16 2 4 2 2 2 2 2 2 6
546 550 557 561 562 563 564 565 566 568 570 572 573 578 583 584 586 591 593 595 597 598 601 607
2 2 2 2 4 2 4 12 4 4 2 2 6 8 2 8 2 80 2 4 2 2 4 2
608 609 613 622 624 635 640 642 644 645 650 652 654 660 662 663 668 669 671 672 682 685 689 693
4 2 6 6 2 2 4 2 4 6 4 4 2 6 4 92 2 2 2 4 2 2 2 2
694 695 696 697 698 701 702 704 706 712 715 728 729 730 731 745 754 764 768 771 776 781 782 783
2 2 4 2 4 2 120 2 6 2 8 4 2 4 2 4 2 4 2 12 4 2 2 4
785 787 790 791 792 793 794 795 796 798 800 801 802 804 806 807 808 812 814 816 819 822 825 827
2 2 2 20 364 6 4 6 2 2 2 6 12 2 2 2 8 8 4 2 2 4 2 4
829 837 838 839 840 841 842 843 844 845 846 847 848 850 852 853 855 857 860 861 863 864 865 866
6 30 74 26 4 2 8 16 6 8 20 4 14 4 10 2 6 2 2 14 6 34 50 12
867 868 869 870 871 873 874 876 877 878 879 880 881 882 884 885 886 888 889 891 892 893 895 896
2 22 2 6 4 8 4 2 4 32 102 40 6 14 28 4 42 2 4 84 10 4 2 2
897 900 901 902 904 905 907 908 909 910 911 913 914 915 916 917 918 919 920 921 922 923 924 925
72 84 6 2 8 4 70 22 18 2 6 2 2 6 4 12 12 12 14 72 1498 8 2 4
926 927 928 932 934 936 937 938 939 941 944 945 948 949 950 951 952 953 954 955 956 957 959 960
2 14 2 4 2 14 2 2 2 20 2 2 2 2 4 42 258 16 22 18 10 2 12 14
961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 981 983 985 986 987
14 2 14 6 10 8 40 10 4 48 26 24 22 18 22 42 308 6 34 2 6 6 8 24
988 989 990 991 992 993 994 995 996 997 998 999
8 16 6 2 2 24 16 4 8 4 14 74
I’ll keep values > 448 (but I retain keeping values ≥ 792 wouldn’t be an error since we have very little values between 448 and 792, similarly to the previous analysis when we talked about the 406-762 “gap”).
minExp <- 448
# Remove confidence values > 448 while keeping only columns of interest for this project
ECOEXP_TRANSF <- as.data.frame(cbind(ECOPPITABLE$protein1[ECOPPITABLE$experiments_transferred>minExp], ECOPPITABLE$protein2[ECOPPITABLE$experiments_transferred>minExp], ECOPPITABLE$experiments_transferred[ECOPPITABLE$experiments_transferred>minExp]))
colnames(ECOEXP_TRANSF) <- c("p1", "p2", "weight")
ECOEXP_TRANSF$weight <- as.numeric(ECOEXP_TRANSF$weight)
For the moment, let’s focus on the network with experimentally verified interactions only.
library(igraph)
GEXP <- graph.data.frame(ECOEXP)
And extract connected components, also having a look at the graph using Cytoscape.
CC <- components(GEXP)
# How many?
length(unique(CC$membership))
[1] 80
we <- get.edge.attribute(GEXP,"weight")
library(RCy3)
library(plyr)
cytoscapePing() # Note that Cytoscape must be installed and run to ping it
You are connected to Cytoscape!
createNetworkFromIgraph(GEXP,new.title='GEXP')
Loading data...
Applying default style...
Applying preferred layout...
networkSUID
124
Alternatively, we can do the above for ECOEXP_TRANSF, getting a much denser (more connected) network. We’ll use the last mentioned network for further analysis.
GEXP_TRANSF <- graph.data.frame(ECOEXP_TRANSF)
CCT <- components(GEXP_TRANSF)
# How many?
length(unique(CCT$membership))
[1] 79
weT <- get.edge.attribute(GEXP_TRANSF,"weight")
# To filter it (we won't need to do it because we have already filtered low confidence values):
# GEXP_TRANSF <- delete_edges(GEXP_TRANSF, edges = which(weT < minExp))
# CCT <- components(GEXP_TRANSF)
createNetworkFromIgraph(GEXP_TRANSF,new.title='GEXP_TRANSF')
Loading data...
Applying default style...
Applying preferred layout...
networkSUID
5388
To identify non-bottleneck hubs and bottleneck hubs we calculate the betweenness centrality (high value == bottleneck).
BET <- betweenness(GEXP_TRANSF, directed=FALSE)
hist(BET)

As they did in papers, we simply define bottlenecks on the basis of the quantile.
# 0.9 threshold to consider a protein a bottleneck in the PPI
BET_TAU <- quantile(BET, 0.9)
GEXP_TRANSF_DEG <- igraph::degree(GEXP_TRANSF)
Then we define HUBS.
# 0.9 threshold to consider a protein a hub in the PPI
DEG_TAU <- quantile(GEXP_TRANSF_DEG, 0.9)
To select bottleneck-hubs (BH) we combine the thresholds to identify a set of proteins fulfilling both.
BH <- V(GEXP_TRANSF)$name[BET>=BET_TAU & GEXP_TRANSF_DEG>=DEG_TAU]
Select non-bottleneck hubs (NBH):
NBH <- V(GEXP_TRANSF)$name[BET<BET_TAU & GEXP_TRANSF_DEG>=DEG_TAU]
Select bottleneck non-hub (BNH):
BNH <- V(GEXP_TRANSF)$name[BET>=BET_TAU & GEXP_TRANSF_DEG<DEG_TAU]
Select non-bottleneck non hubs NBNH:
NBNH <- V(GEXP_TRANSF)$name[BET<BET_TAU & GEXP_TRANSF_DEG<DEG_TAU]
Check the length of the vectors:
c(
length(BH), length(NBH), length(BNH), length(NBNH)
)
[1] 13 33 32 369
Non-bottleneck non-hubs (NBNH) are the largest group as said by the very beginning.
In this project, instead of having to do with a list of essential genes such that you can simply calculate the length of the intersection, we need to calculate a correlation coefficient distribution for all the interactors of the proteins within each of the BH and NBH (in this project we focus on hubs). Therefore, at this point we download gene expression levels from a provided gene expression compendium for E. coli, then we’ll match protein names here in the graph and in the compendium.
load(url("https://github.com/emanuelecavalleri/partyANDdatePPnetworks/blob/main/Ecoli_compendium.RData?raw=true")) # From now on we'll focus on ZL2 matrix, but we have 3 different versions of a gene expression compendium for E. coli (we could've used ALL_FINAL_Nreads and ALL_FINAL_TPM too). Moreover, we don’t care on conditions (columns of the matrices) in this project
For instance, to have an idea about the content of the matrix, here we report the 6 first conditions’ gene expression levels for the entry b3065, which is a small subunit ribosomal protein belonging to the bacterial ribosomal protein bS21 family also known as “rpsU”.
head(ZL2["b3065",])
DRR091867 DRR091875 DRR091882 DRR100424 DRR100428 ERR1025366
1.9681146 0.9748877 0.2629133 0.7247111 1.6982523 -0.2744939
For the sake of completeness here we add STRING and AlphaFold entries relative to b3065/rpsU.
We are interested in how much the interactors are correlated; before the loop to calculate the pearsons for each member of the bottleneck hub (BH) group, we define:
fixed_intervals <- seq(-1.05, 1.05, .1)
allpearsonsBH <- matrix(0, ncol=length(fixed_intervals)-1, nrow=length(BH))
Now iterate over the BH:
for(i in 1:length(BH)){
# Neighbor genes such that we can extract the corresponding rows
# from the compendium
hub_interactors <- neighborhood(GEXP_TRANSF,nodes=BH[i])[[1]]
# BH[i] is the first in the hub_interactors list of interactors
# extracted with neighborhood, therefore it is in first row;
# we are interested in the correlation of interactors of BH[i]
# when BH[i] is "present", therefore:
match <- match(names(hub_interactors), rownames(ZL2))
extractedrows <- ZL2[match,]
bhi_expressed <- which(extractedrows[1,] > mean(extractedrows[1,]))
extractedrows <- extractedrows[,bhi_expressed]
# Remove the hub under analysis:
extractedrows <- extractedrows[-1,]
# Calculate a matrix of correlation of all proteins interacting
# with BH[i] vs themselves in all combinations:
pearsons <- cor(t(extractedrows))
# Additionally we treat all of them at once, we transform it into a vector
pearsons <- pearsons[upper.tri(pearsons)]
# Now we store the distribution of pearsons for each member
# of the category (here BH) and then put them together;
# this can be done by putting all pearson values together for all members
# of a group and then plot histograms.
# In doing so however, hubs with larger number of interactors will get more weight.
par(mfrow=c(1,2)) # Setting the plotting area into a 1*2 array
if(i >=1 && i <= 3){
# We'll show you only the first 3 plots for ease of visualization.
# To give the same weight to the distribution of pearsons value for different hubs:
h <- hist(pearsons, breaks=fixed_intervals)
# h$counts/sum(h$counts) and h$mids indicates the middle point of each interval.
# Then, within the loop, we calculate the above histogram
plot(h$mids, h$counts/sum(h$counts))
}
else
h <- hist(pearsons, breaks=fixed_intervals, plot=FALSE)
# and store pearsons correlation coefficients:
allpearsonsBH[i,] <- h$counts/sum(h$counts)
}



At the end we have allpearsonsBH populated by the values of all members of the group. Are all BH similar/different for the way their targets are correlated?
library(pheatmap)
colnames(allpearsonsBH) <- h$mids
pheatmap(allpearsonsBH, cluster_cols = FALSE)

For party hubs most interactors should be coexpressed at the same time,: we notice a peak on the right (the previous choice of a high threshold highlights this fact).
Conversely, date hubs interact with different interactors at different times, therefore the distribution of correlations among the interactors should be more flat or with several peaks.
allpearsonsNBH <- matrix(0, ncol=length(fixed_intervals)-1, nrow=length(NBH))
for(i in 1:length(NBH)){
hub_interactors <- neighborhood(GEXP_TRANSF,nodes=NBH[i])[[1]]
match <- match(names(hub_interactors), rownames(ZL2))
extractedrows <- ZL2[match,]
nbhi_expressed <- which(extractedrows[1,] > mean(extractedrows[1,]))
extractedrows <- extractedrows[,nbhi_expressed]
extractedrows <- extractedrows[-1,]
pearsons <- cor(t(extractedrows))
pearsons <- pearsons[upper.tri(pearsons)]
par(mfrow=c(1,2))
if(i >= 1 && i <= 3){
h <- hist(pearsons, breaks=fixed_intervals)
plot(h$mids, h$counts/sum(h$counts))
}
else
h <- hist(pearsons, breaks=fixed_intervals, plot=FALSE)
allpearsonsNBH[i,] <- h$counts/sum(h$counts)
}



colnames(allpearsonsNBH) <- h$mids
pheatmap(allpearsonsNBH, cluster_cols = FALSE)

We can confirm our hypothesis: we can identify peaks in the heatmap and a more “scattered” plot. The fact that several peaks are present may be an indication of multiple complexes formed by the hub and also of many different interactions with one or a few partners at a time.
Let’s now see if there is some inner structure by using some dimensionality reduction technique such as UMAP and PCA on BH and NBH groups: we’ll focus on UMAP first.
library(uwot)
UBH <- umap(allpearsonsBH, n_components = 3, n_neighbors = 2)
library(rgl)
options(rgl.useNULL = TRUE)
plot3d(x=UBH[,1], y=UBH[,2], z=UBH[,3], col="red")
rglwidget()
UNBH <- umap(allpearsonsNBH, n_components = 3)
plot3d(x=UNBH[,1], y=UNBH[,2], z=UNBH[,3], col="green")
rglwidget()
And what if we join the allpearsonsBH and allpearsonsNBH and do the same? We can also color BH and NBH items differently to see if they group together.
allpearsons <- rbind(allpearsonsBH, allpearsonsNBH)
U <- umap(allpearsons, n_components = 3)
plot3d(x=U[,1], y=U[,2], z=U[,3], col = c(rep("red", length(BH)), rep("green", length(NBH))))
legend3d("topright", legend = c('BH', 'NBH'), pch = 16, col = c("red", "green"), cex=1, inset=c(0.02))
rglwidget()
We can recognize three groups of interest in the space. Focusing on BH points we can notice that the 13 points belonging to that category are not all grouped together, but we can see that the majority are within a precise region of the 3D space and that the three/four outside the “condensed” region seem to follow a precise trajectory toward the other two NBH groups, so maybe a sign of similar behavior. In other words, BH points are not isolated in the space, but within regions also populated by NBH points (at least one BH point in any NBH region).
Now let’s focus onto PCA technique.
PCA <- prcomp(allpearsons)
scores = as.data.frame(PCA$x)
library(ggplot2)
ggplot(data = scores, aes(x = PC1, y = PC2, label = rownames(scores))) +
geom_hline(yintercept = 0, colour = "gray65") +
geom_vline(xintercept = 0, colour = "gray65") +
geom_text(colour = c(rep("red", length(BH)), rep("green", length(NBH))), alpha = 0.8, size = 4)

PCA makes it even clearer definitely confirming our results previously obtained: red points (coming from the BH group), except for points 2 and 3 and if you want 13 (similarly to the 3/4 points distant from the most populated group in UMAP 3D plot), tend to group together, while other ones (belonging to the NBH group) tend to be sparser (curiously similar to the fact that previously we obtained a more “scattered” heatmap).
Session infos:
cytoscapeVersionInfo()
apiVersion cytoscapeVersion
"v1" "3.9.0"
sessionInfo()
R version 4.0.4 (2021-02-15)
Platform: x86_64-apple-darwin17.0 (64-bit)
Running under: macOS Big Sur 10.16
Matrix products: default
LAPACK: /Library/Frameworks/R.framework/Versions/4.0/Resources/lib/libRlapack.dylib
locale:
[1] it_IT.UTF-8/it_IT.UTF-8/it_IT.UTF-8/C/it_IT.UTF-8/it_IT.UTF-8
attached base packages:
[1] stats graphics grDevices utils datasets methods base
other attached packages:
[1] ggplot2_3.3.5 rgl_0.108.3 uwot_0.1.11 Matrix_1.4-0 pheatmap_1.0.12 plyr_1.8.6 RCy3_2.10.2
[8] igraph_1.2.10
loaded via a namespace (and not attached):
[1] Rcpp_1.0.7 lattice_0.20-45 FNN_1.1.3 assertthat_0.2.1 digest_0.6.29
[6] utf8_1.2.2 RSpectra_0.16-0 mime_0.12 R6_2.5.1 stats4_4.0.4
[11] evaluate_0.14 httr_1.4.2 pillar_1.6.4 rlang_0.4.12 curl_4.3.2
[16] irlba_2.3.5 jquerylib_0.1.4 R.utils_2.11.0 R.oo_1.24.0 rmarkdown_2.11
[21] labeling_0.4.2 stringr_1.4.0 htmlwidgets_1.5.4 munsell_0.5.0 compiler_4.0.4
[26] xfun_0.29 pkgconfig_2.0.3 BiocGenerics_0.36.1 htmltools_0.5.2 tidyselect_1.1.1
[31] tibble_3.1.6 XML_3.99-0.8 fansi_0.5.0 crayon_1.4.2 dplyr_1.0.7
[36] withr_2.4.3 R.methodsS3_1.8.1 grid_4.0.4 jsonlite_1.7.2 gtable_0.3.0
[41] lifecycle_1.0.1 DBI_1.1.2 magrittr_2.0.1 scales_1.1.1 graph_1.68.0
[46] stringi_1.7.6 farver_2.1.0 bslib_0.3.1 ellipsis_0.3.2 vctrs_0.3.8
[51] generics_0.1.1 RColorBrewer_1.1-2 tools_4.0.4 RJSONIO_1.3-1.6 glue_1.6.0
[56] purrr_0.3.4 parallel_4.0.4 fastmap_1.1.0 yaml_2.2.1 colorspace_2.0-2
[61] knitr_1.37 sass_0.4.0
LS0tCnRpdGxlOiAiUGFydHkgYW5kIGRhdGUgaHVicyBpbiBwcm90ZWluLXByb3RlaW4gaW50ZXJhY3Rpb24gbmV0d29ya3MiCnN1YnRpdGxlOiAiQWR2YW5jZWQgZ2Vub21pY3MnIFVOSU1JIHByb2plY3QgKEFZIDIwMjEvMjAyMikiCmF1dGhvcjoKICAtIG5hbWU6ICJFbWFudWVsZSBDYXZhbGxlcmkgKG1hdHJpY3VsYXRpb24gbnVtYmVyOiA5ODU4ODgpIgogICAgYWZmaWxpYXRpb246ICJbVW5pdmVyc2l0w6AgZGVnbGkgU3R1ZGkgZGkgTWlsYW5vXShodHRwczovL3d3dy51bmltaS5pdC9lbikiCm91dHB1dDogaHRtbF9ub3RlYm9vayAKLS0tCgpUaGlzIGlzIG15IEFkdmFuY2VkIGdlbm9taWNzJyBwcm9qZWN0IGZvciB0aGUgQVkgMjAyMS8yMDIyLiBEYXRhIGFuZCBjb2RlIGNhbiBiZSByZXRyaWV2ZWQgZnJvbSB0aGUgZm9sbG93aW5nIFtHaXRIdWJdKGh0dHBzOi8vZ2l0aHViLmNvbS9lbWFudWVsZWNhdmFsbGVyaS9wYXJ0eUFORGRhdGVQUG5ldHdvcmtzKSBsaW5rLgoKSW4gcHJvdGVpbi1wcm90ZWluIGludGVyYWN0aW9uIChbUFBJXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9Qcm90ZWluJUUyJTgwJTkzcHJvdGVpbl9pbnRlcmFjdGlvbikgZnJvbSBub3cgb24pIG5ldHdvcmtzICh0aGVyZWZvcmUgcHJvdGVpbnMgdGhhdCBpbnRlcmFjdCB3aXRoIG1hbnkgb3RoZXIgcHJvdGVpbnMpIHdlIGRpc3Rpbmd1aXNoIFtodWJzXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9JbnRlcmFjdG9tZSNIdWJzKSAoaGlnaGx5IGNvbm5lY3RlZCBub2Rlcy9wcm90ZWlucykgaW50byBwYXJ0eSBhbmQgZGF0ZSBjYXRlZ29yaWVzLiBUaGlzIGRpZmZlcmVudGlhdGlvbiBjb21lcyBmcm9tIGFuIFthbmFseXNpc10oaHR0cHM6Ly93d3cubmF0dXJlLmNvbS9hcnRpY2xlcy9uYXR1cmUwMjU1NSkgKEhhbiBldCBhbC4pIHdoZXJlIGl0IHdhcyBzaG93biB0aGF0IGh1YnMgb2YgdGhlIFBQSSBuZXR3b3JrIGNhbiBiZSBhc3NpZ25lZCB0byB0d28gZGlmZmVyZW50IGNhdGVnb3JpZXM6XArCtyBIdWJzIHRoYXQgaW50ZXJhY3Qgd2l0aCBtYW55IHBhcnRuZXJzIGF0IHRoZSBzYW1lIG1vbWVudCAocGFydHkgaHVicyksICB3aGljaCBhbHNvIGNvbm5lY3QgcHJvdGVpbnMgd2l0aGluIGZ1bmN0aW9uYWwgbW9kdWxlcyBzdWNoIGFzIHByb3RlaW4gY29tcGxleGVzLiBQYXJ0eSBodWJzIGNvbXBvbmVudHMnIGV4cHJlc3Npb24gaXMgY29ycmVsYXRlZCB3aXRoIGl0cyBpbnRlcmFjdGlvbiBwYXJ0bmVycy5cCsK3IEh1YnMgdGhhdCBoYXZlIG1hbnkgdGFyZ2V0cywgYnV0IGludGVyYWN0IHdpdGggb25seSBvbmUgb3IgYSBmZXcgb2YgdGhlbSBhdCBhbnkgbW9tZW50IChkYXRlIGh1YnMpLiBJbiBjb250cmFzdCB0byBwYXJ0eSBodWJzLCBkYXRlIGh1YnMgZG8gbm90IGV4aGliaXQgc3VjaCBhIGNvcnJlbGF0aW9uIGFuZCBhcHBlYXIgdG8gY29ubmVjdCBkaWZmZXJlbnQgZnVuY3Rpb25hbCBtb2R1bGVzLlwKQSBwb3B1bGFyIFBQSSBuZXR3b3JrcyBmdW5jdGlvbmFsIGVucmljaG1lbnQgYW5hbHlzaXMgdG9vbCBpcyBbU1RSSU5HXShodHRwczovL3N0cmluZy1kYi5vcmcvKS4KClRoZSB2YWxpZGl0eSBvZiB0aGUgZGF0ZSBodWIvcGFydHkgaHViIGRpc3RpbmN0aW9uIHdhcyBkaXNwdXRlZCwgdGhlIGNvbmNlcHQgbWlnaHQgbm90IGJlIGFzIGdlbmVyYWwgYXMgaXQgc2VlbWVkIHdoZW4gdGhlIG9yaWdpbmFsIHB1YmxpY2F0aW9uIHdhcyBwcmVzZW50ZWQuIEluIHBhcnRpY3VsYXIsIGluIHRoaXMgW3BhcGVyXShodHRwczovL2RvaS5vcmcvMTAuMTM3MS9qb3VybmFsLnBjYmkuMTAwMDgxNyksIHRoZSBhdXRob3JzIHNob3dlZCB0aGF0IHRoZSByZXBvcnRlZCBpbXBvcnRhbmNlIG9mIGRhdGUgaHVicyB0byBuZXR3b3JrIGNvbm5lY3Rpdml0eSBjYW4gaW4gZmFjdCBiZSBhdHRyaWJ1dGVkIHRvIGEgdGlueSBzdWJzZXQgb2YgdGhlbS5cClRoZSBkYXRlL3BhcnR5IGRpc3RpbmN0aW9uIHdhcyBvcmlnaW5hbGx5IG1vdGl2YXRlZCBieSBhbiBhcHByb3hpbWF0ZWx5IGJpbW9kYWwgZGlzdHJpYnV0aW9uIG9mIGh1YiBjby1leHByZXNzaW9uLCBidXQgdGhpcyBmZWF0dXJlIGlzIG5vdCBhbHdheXMgcm9idXN0IHRvIG1ldGhvZG9sb2dpY2FsIGNoYW5nZXMuIEFkZGl0aW9uYWxseSwgdG9wb2xvZ2ljYWwgcHJvcGVydGllcyBvZiBodWJzIGRvIG5vdCBpbiBnZW5lcmFsIGNvcnJlbGF0ZSB3aXRoIGNvLWV4cHJlc3Npb24uIEFjY29yZGluZyB0byB0aGUgcGFwZXIsICp0aGlua2luZyBpbiB0ZXJtcyBvZiBhIGRhdGUvcGFydHkgZGljaG90b215IGZvciBodWJzIGluIHByb3RlaW4gaW50ZXJhY3Rpb24gbmV0d29ya3MgaXMgbm90IG1lYW5pbmdmdWwsIGFuZCBpdCBtaWdodCBiZSBtb3JlIHVzZWZ1bCB0byBjb25jZWl2ZSBvZiByb2xlcyBmb3IgcHJvdGVpbi1wcm90ZWluIGludGVyYWN0aW9ucyByYXRoZXIgdGhhbiBmb3IgaW5kaXZpZHVhbCBwcm90ZWlucyouCgpUaGlzIHByb2plY3QgaXMgYmFzZWQgb24gdGhlIGZhY3QgdGhhdCB0aGUgcGFydHkgYW5kIGRhdGUgaHViIGNvbmNlcHQgbWF5IGJlIGludGVyZXN0aW5nIHRvIGJlIG1lcmdlZCB3aXRoIGFub3RoZXIgdG9wb2xvZ2ljYWwgcHJvcGVydHkgb2Ygbm9kZXMsIFtiZXR3ZWVubmVzcyBjZW50cmFsaXR5XShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9DZW50cmFsaXR5I0JldHdlZW5uZXNzX2NlbnRyYWxpdHkpLCB0aGF0IHdlIGNhbiB1c2UgdG8gaW50cm9kdWNlIHRoZSBib3R0bGVuZWNrIGNvbmNlcHQgaW50byB0aGUgcGFydHkvZGF0ZSBodWJzIGNvbmNlcHR1YWxpemF0aW9uLiBbKkJldHdlZW5uZXNzIChpLmUuLCDigJxib3R0bGVuZWNrLW5lc3PigJ0pIGlzIGEgbXVjaCBtb3JlIHNpZ25pZmljYW50IGluZGljYXRvciBvZiBlc3NlbnRpYWxpdHkgdGhhbiBkZWdyZWUgKGkuZS4sIOKAnGh1Yi1uZXNz4oCdKS4gRnVydGhlcm1vcmUsIGJvdHRsZW5lY2tzIGNvcnJlc3BvbmQgdG8gdGhlIGR5bmFtaWMgY29tcG9uZW50cyBvZiB0aGUgaW50ZXJhY3Rpb24gbmV0d29ya+KAlHRoZXkgYXJlIHNpZ25pZmljYW50bHkgbGVzcyB3ZWxsIGNvZXhwcmVzc2VkIHdpdGggdGhlaXIgbmVpZ2hib3JzIHRoYW4gbm9uLWJvdHRsZW5lY2tzLCBpbXBseWluZyB0aGF0IGV4cHJlc3Npb24gZHluYW1pY3MgaXMgd2lyZWQgaW50byB0aGUgbmV0d29yayB0b3BvbG9neS4qXShodHRwczovL2RvaS5vcmcvMTAuMTM3MS9qb3VybmFsLnBjYmkuMDAzMDA1OS4pCgpUaGVyZWZvcmUsIGNvbWJpbmluZyBodWJzIGFuZCBib3R0bGVuZWNrcywgd2UgY2FuIGRlZmluZSBmb3IgaW5zdGFuY2UgdGhlIGZvbGxvd2luZyBjYXRlZ29yaWVzIG9mIHByb3RlaW5zLCBkZWZpbmVkIG9uIHRvcG9sb2dpY2FsIHByb3BlcnRpZXM6IG5vbi1ib3R0bGVuZWNrIGh1YnMgKE5CSCksIGJvdHRsZW5lY2sgaHVicyAoQkgpLCBib3R0bGVuZWNrIG5vbi1odWJzIChCTkgpIGFuZCBub24tYm90dGxlbmVjayBub24taHVicyAoTkJOSCwgdGhlIGxhcmdlc3QgZ3JvdXAgYnkgZGVmaW5pdGlvbikuIAoKIVtdKHBjYmkuMDAzMDA1OS5nMDAxLlBOR19MLnBuZykKClRoaXMgcHJvamVjdCBhaW1zIGF0IGludGVncmF0aW5nIHRoZSA0IGNhdGVnb3JpZXMgYWJvdmUgd2l0aCB0aGUgaWRlYSBvZiBwYXJ0eSBhbmQgZGF0ZSBodWJzIHVzaW5nIGFuIGludGVyYWN0b21lIGZvciBFLiBjb2xpIGFuZCBhIGdlbmUgZXhwcmVzc2lvbiBjb21wZW5kaXVtIChzaW5jZSB0aGUgaW50ZXJhY3Rpb24gYW1vbmcgdHdvIHByb3RlaW5zIGNhbiBvbmx5IHRha2UgcGxhY2UgaWYgdGhlIHR3byBwcm90ZWlucyBhcmUgZXhwcmVzc2VkIGF0IHRoZSBzYW1lIG1vbWVudCwgb25lIGNhbiB1c2UgZ2VuZSBleHByZXNzaW9uIGNvbXBlbmRpYSB0byBhc3NpZ24gdGhlIGh1YnMgdG8gdGhlIHBhcnR5IG9yIGRhdGUgY2xhc3MpLiAgVGhlIG5ldHdvcmsgaXMgdXNlZCB0byBkZWZpbmUgdGhlIGFib3ZlIGdyb3VwcyBiYXNlZCBvbiB0aGUgdHdvIHRvcG9sb2dpY2FsIHByb3BlcnRpZXMgKGRlZ3JlZSwgdG8gZGVmaW5lIGh1Ym5lc3MsIGFuZCBiZXR3ZWVubmVzcyBjZW50cmFsaXR5LCB0byBkZWZpbmUgYm90dGxlbmVja25lc3MpLgoKTGV0J3Mgc3RhcnQgcmVhZGluZyBbRS4gQ29saSBwcm90ZWluIHBoeXNpY2FsIGxpbmtzXShodHRwczovL2dpdGh1Yi5jb20vZW1hbnVlbGVjYXZhbGxlcmkvcGFydHlBTkRkYXRlUFBuZXR3b3Jrcy9ibG9iL21haW4vNTExMTQ1LnByb3RlaW4ucGh5c2ljYWwubGlua3MuZnVsbC52MTEuMC50eHQ/cmF3PXRydWUpIHR4dCBmaWxlLi4uCmBgYHtyfQpybShsaXN0PWxzKCkpICMgSnVzdCBnb29kIHByYWN0aWNlIHRvIGVuc3VyZSB3ZSdsbCB3b3JrIG9uIGEgImNsZWFuIiBlbnZpcm9ubWVudApFQ09QUElUQUJMRSA8LSByZWFkLnRhYmxlKCJodHRwczovL2dpdGh1Yi5jb20vZW1hbnVlbGVjYXZhbGxlcmkvcGFydHlBTkRkYXRlUFBuZXR3b3Jrcy9ibG9iL21haW4vNTExMTQ1LnByb3RlaW4ucGh5c2ljYWwubGlua3MuZnVsbC52MTEuMC50eHQ/cmF3PXRydWUiLCBoZWFkZXI9VFJVRSkKYGBgCmFuZCBoYXZlIGEgbG9vayBhdCBpdC4KYGBge3J9CmhlYWQoRUNPUFBJVEFCTEUpCmBgYAoqRUNPUFBJVEFCTEUqIGNvbnRhaW5zIGV4cGVyaW1lbnRhbGx5IGRldGVybWluZWQgaW50ZXJhY3Rpb25zLiBUaGUgKmV4cGVyaW1lbnRzKiBjb2x1bW4gaW5kaWNhdGVzIHNvbWUgc29ydCBvZiAiY29uZmlkZW5jZSIgaW4gdGhlIGV4cGVyaW1lbnQuIFdlIHdhbnQgdG8gaGF2ZSBhIGZpbHRlciBhdCBzb21lIHZhbHVlIHRvIGluY3JlYXNlIHRoZSBjb25maWRlbmNlLCBhbmQgdG8gYWNjb21wbGlzaCB0aGF0IHdlIGZpcnN0IHRha2UgYSBsb29rIGF0IHRoZSAidHJlbmQiIG9mIGNvbmZpZGVuY2UgdmFsdWVzLgpgYGB7cn0KIyBFQ09QUElUQUJMRSdzIGRpbWVuc2lvbnMKZGltKEVDT1BQSVRBQkxFKQojIENvbmZpZGVuY2UgdmFsdWVzID4gMDogCmxlbmd0aChFQ09QUElUQUJMRSRleHBlcmltZW50c1sod2hpY2goRUNPUFBJVEFCTEUkZXhwZXJpbWVudHMgPiAwKSldKQojIFZhbHVlIG9mIHRoZSBtaW4gY29uZmlkZW5jZSB2YWx1ZSA+IDA6Cm1pbihFQ09QUElUQUJMRSRleHBlcmltZW50c1sod2hpY2goRUNPUFBJVEFCTEUkZXhwZXJpbWVudHMgPiAwKSldKQojIE51bWJlciBvZiB2YWx1ZXMgYWJvdmUgY2VydGFpbiB0aHJlc2hvbGQgKHVzZWZ1bCB0byBleHRyYWN0IHZhbHVlcycgInRyZW5kIikgCmMoCiAgbGVuZ3RoKEVDT1BQSVRBQkxFJGV4cGVyaW1lbnRzWyh3aGljaChFQ09QUElUQUJMRSRleHBlcmltZW50cyA+IDQ2KSldKSwKICBsZW5ndGgoRUNPUFBJVEFCTEUkZXhwZXJpbWVudHNbKHdoaWNoKEVDT1BQSVRBQkxFJGV4cGVyaW1lbnRzID4gMTAwKSldKSwKICBsZW5ndGgoRUNPUFBJVEFCTEUkZXhwZXJpbWVudHNbKHdoaWNoKEVDT1BQSVRBQkxFJGV4cGVyaW1lbnRzID4gMjAwKSldKSwKICBsZW5ndGgoRUNPUFBJVEFCTEUkZXhwZXJpbWVudHNbKHdoaWNoKEVDT1BQSVRBQkxFJGV4cGVyaW1lbnRzID4gMzAwKSldKSwKICBsZW5ndGgoRUNPUFBJVEFCTEUkZXhwZXJpbWVudHNbKHdoaWNoKEVDT1BQSVRBQkxFJGV4cGVyaW1lbnRzID4gNDAwKSldKSwKICBsZW5ndGgoRUNPUFBJVEFCTEUkZXhwZXJpbWVudHNbKHdoaWNoKEVDT1BQSVRBQkxFJGV4cGVyaW1lbnRzID4gNTAwKSldKSwKICBsZW5ndGgoRUNPUFBJVEFCTEUkZXhwZXJpbWVudHNbKHdoaWNoKEVDT1BQSVRBQkxFJGV4cGVyaW1lbnRzID4gNjAwKSldKSwKICBsZW5ndGgoRUNPUFBJVEFCTEUkZXhwZXJpbWVudHNbKHdoaWNoKEVDT1BQSVRBQkxFJGV4cGVyaW1lbnRzID4gNzAwKSldKSwKICBsZW5ndGgoRUNPUFBJVEFCTEUkZXhwZXJpbWVudHNbKHdoaWNoKEVDT1BQSVRBQkxFJGV4cGVyaW1lbnRzID4gODAwKSldKQopCiMgTWF4IGNvbmZpZGVuY2UgdmFsdWUKbWF4KEVDT1BQSVRBQkxFJGV4cGVyaW1lbnRzKSAKYGBgCkF0IGEgZmlyc3QgZ2xpbXBzZSwgYnkgbG9va2luZyBhdCB0aGUgbnVtYmVyIG9mIGVudHJpZXMgZm9yIGVhY2ggc3Vic2V0LCB3ZSBjYW4gc2VlIG51bWJlciBvZiBpdGVtcyByZWFjaGluZyBhIHBsYXRlYXUgYXJvdW5kIHZhbHVlcyA+IDQwMCwgaW4gcGFydGljdWxhcjoKYGBge3J9CiMgVmFsdWUgb2YgdGhlIG1pbiBjb25maWRlbmNlIHZhbHVlID4gNDAwOgptaW4oRUNPUFBJVEFCTEUkZXhwZXJpbWVudHNbKHdoaWNoKEVDT1BQSVRBQkxFJGV4cGVyaW1lbnRzID4gNDAwKSldKQojIFZhbHVlIG9mIHRoZSBtaW4gY29uZmlkZW5jZSB2YWx1ZSA+IDQwNjoKbWluKEVDT1BQSVRBQkxFJGV4cGVyaW1lbnRzWyh3aGljaChFQ09QUElUQUJMRSRleHBlcmltZW50cyA+IDQwNikpXSkKYGBgCkdyZWF0IGdhcCEKYGBge3J9CiMgTnVtYmVyIG9mIHZhbHVlcyBhYm92ZSA0MDYgKHZhbHVlcyDiiaUgNzYyKSB0aHJlc2hvbGQKbGVuZ3RoKEVDT1BQSVRBQkxFJGV4cGVyaW1lbnRzWyh3aGljaChFQ09QUElUQUJMRSRleHBlcmltZW50cyA+IDQwNikpXSkKYGBgClRoZSBwbGF0ZWF1IHN0YXJ0cyBhdCBjb25maWRlbmNlIHZhbHVlID0gNzYyOyB0aGlzICJlbXBpcmljYWwiIHN0dWZmIGhlbHBlZCBtZSByZXRhaW4gYW4gYXBwcm9wcmlhdGUgdGhyZXNob2xkIG1pZ2h0IGJlIDQwNi83NjIuIEJ1dCBsZXQncyBnbyBpbnRvIGZ1cnRoZXIgZGV0YWlscyBhbmQgbWFrZSBhIGhpc3RvZ3JhbSBhbmQgYSBiYXJwbG90LCBhZnRlciB0aGUgcmVtb3ZhbCBvZiBjb25maWRlbmNlIHZhbHVlcyA9IDAuCmBgYHtyfQptaW5FeHAgPC0gMAojIFJlbW92ZSBjb25maWRlbmNlIHZhbHVlcyA9IDAgd2hpbGUga2VlcGluZyBvbmx5IGNvbHVtbnMgb2YgaW50ZXJlc3QgZm9yIHRoaXMgcHJvamVjdApFQ09FWFAgPC0gYXMuZGF0YS5mcmFtZShjYmluZChFQ09QUElUQUJMRSRwcm90ZWluMVtFQ09QUElUQUJMRSRleHBlcmltZW50cz5taW5FeHBdLCBFQ09QUElUQUJMRSRwcm90ZWluMltFQ09QUElUQUJMRSRleHBlcmltZW50cz5taW5FeHBdLCBFQ09QUElUQUJMRSRleHBlcmltZW50c1tFQ09QUElUQUJMRSRleHBlcmltZW50cz5taW5FeHBdKSkKY29sbmFtZXMoRUNPRVhQKSA8LSBjKCJwMSIsICJwMiIsICJ3ZWlnaHQiKQpFQ09FWFAkd2VpZ2h0IDwtIGFzLm51bWVyaWMoRUNPRVhQJHdlaWdodCkKCmJhcnBsb3QoRUNPRVhQJHdlaWdodCwgeWxhYj0iQ29uZmlkZW5jZSIsIHlsaW09YygwLDEwMDApKQpoaXN0KEVDT0VYUCR3ZWlnaHQsIHhsYWI9IkNvbmZpZGVuY2UiLCB4bGltPWMoMCwxMDAwKSwgeWxpbT1jKDAsMjAwMCksIG1haW49Ikhpc3RvZ3JhbSBvZiBjb25maWRlbmNlIHZhbHVlcyIsIGNvbCA9IGMocmVwKCJncmF5IiwgOSksIHJlcCgicmVkIiwgOSkpKQpsZWdlbmQoInRvcHJpZ2h0IiwgYygiQ29uZmlkZW5jZSB2YWx1ZXMg4omkIDQwNiIsICJDb25maWRlbmNlIHZhbHVlcyA+IDQwNiIpLCBjb2w9YygiZ3JheSIsICJyZWQiKSwgbHdkPTEwKQpgYGAKRnJvbSB0aGUgYmFycGxvdCB3ZSBjYW4gc2VlIHRoYXQgYWJvdmUgNDAwIHdlIGhhdmUgbGVzcyAiZGVuc2l0eSIgKGR1ZSB0byB0aGUgNDA2LTc2MiAiZ2FwIiB3ZSB0YWxrZWQgYmVmb3JlKSwgYW5kIGZyb20gdGhlIGhpc3RvZ3JhbSB3ZSBjb25maXJtIG91ciBlbXBpcmljYWwgcGxhdGVhdSBoeXBvdGhlc2lzLiBCZWluZyBzYWlkIHRoYXQsIHdlIGNvbnRpbnVlIG91ciBhbmFseXNpcyB3aXRoIGEgPiA0MDYgKG9yLCBlcXVhbGx5LCDiiaUgNzYyKSBjb25maWRlbmNlIHRocmVzaG9sZCAobWF5IGJlIHRvbyBoaWdoL2hhdmUgdG9vIGxpdHRsZSBpbnRlcmFjdGlvbnM/IFJlc3VsdHMgd2l0aCBhIGxvd2VyIHRocmVzaG9sZCBzaG93ZWQgc2ltaWxhciByZXN1bHRzLCBidXQgd2l0aCBhIGhpZ2hlciBjb21wdXRhdGlvbmFsIGNvbXBsZXhpdHkpLgpgYGB7cn0KbWluRXhwIDwtIDQwNgojIFJlbW92ZSBjb25maWRlbmNlIHZhbHVlcyA+IDQwNiB3aGlsZSBrZWVwaW5nIG9ubHkgY29sdW1ucyBvZiBpbnRlcmVzdCBmb3IgdGhpcyBwcm9qZWN0CkVDT0VYUCA8LSBhcy5kYXRhLmZyYW1lKGNiaW5kKEVDT1BQSVRBQkxFJHByb3RlaW4xW0VDT1BQSVRBQkxFJGV4cGVyaW1lbnRzPm1pbkV4cF0sIEVDT1BQSVRBQkxFJHByb3RlaW4yW0VDT1BQSVRBQkxFJGV4cGVyaW1lbnRzPm1pbkV4cF0sIEVDT1BQSVRBQkxFJGV4cGVyaW1lbnRzW0VDT1BQSVRBQkxFJGV4cGVyaW1lbnRzPm1pbkV4cF0pKQpjb2xuYW1lcyhFQ09FWFApIDwtIGMoInAxIiwgInAyIiwgIndlaWdodCIpCkVDT0VYUCR3ZWlnaHQgPC0gYXMubnVtZXJpYyhFQ09FWFAkd2VpZ2h0KQpgYGAKQmVsb3csIHdlIGFsc28gZGVmaW5lIGEgbXVjaCBsYXJnZXIgbmV0d29yayBvYnRhaW5lZCBieSB0cmFuc2ZlcnJpbmcgZXhwZXJpbWVudGFsIGluZm9ybWF0aW9uIGZyb20gb3RoZXIgc3BlY2llcyAoKmV4cGVyaW1lbnRzX3RyYW5zZmVycmVkKiBjb2x1bW4gaW52b2x2ZWQgaGVyZSkuIEFzIGFib3ZlLCB3ZSBmaWx0ZXIgaXQgdG8gcmV0YWluIHZhbHVlcyBmb3Igd2hpY2ggY29uZmlkZW5jZSBpcyByZWxhdGl2ZWx5IGhpZ2gsIHNvIGxldCdzIGhhdmUgYSBsb29rIGF0IHRoZSB0cmVuZC4KYGBge3J9Cm1pbkV4cCA8LSAwCiMgUmVtb3ZlIGNvbmZpZGVuY2UgdmFsdWVzID0gMCB3aGlsZSBrZWVwaW5nIG9ubHkgY29sdW1ucyBvZiBpbnRlcmVzdCBmb3IgdGhpcyBwcm9qZWN0CkVDT0VYUF9UUkFOU0YgPC0gYXMuZGF0YS5mcmFtZShjYmluZChFQ09QUElUQUJMRSRwcm90ZWluMVtFQ09QUElUQUJMRSRleHBlcmltZW50c190cmFuc2ZlcnJlZD5taW5FeHBdLCBFQ09QUElUQUJMRSRwcm90ZWluMltFQ09QUElUQUJMRSRleHBlcmltZW50c190cmFuc2ZlcnJlZD5taW5FeHBdLCBFQ09QUElUQUJMRSRleHBlcmltZW50c190cmFuc2ZlcnJlZFtFQ09QUElUQUJMRSRleHBlcmltZW50c190cmFuc2ZlcnJlZD5taW5FeHBdKSkKY29sbmFtZXMoRUNPRVhQX1RSQU5TRikgPC0gYygicDEiLCAicDIiLCAid2VpZ2h0IikKRUNPRVhQX1RSQU5TRiR3ZWlnaHQgPC0gYXMubnVtZXJpYyhFQ09FWFBfVFJBTlNGJHdlaWdodCkKCiMgQ29uZmlkZW5jZSB2YWx1ZXMgPiAwOgpsZW5ndGgoRUNPRVhQX1RSQU5TRiR3ZWlnaHRbKHdoaWNoKEVDT0VYUF9UUkFOU0Ykd2VpZ2h0ID4gMCkpXSkKIyBWYWx1ZSBvZiB0aGUgbWluIGNvbmZpZGVuY2UgdmFsdWUgPiAwOgptaW4oRUNPRVhQX1RSQU5TRiR3ZWlnaHRbKHdoaWNoKEVDT0VYUF9UUkFOU0Ykd2VpZ2h0ID4gMCkpXSkKIyBOdW1iZXIgb2YgdmFsdWVzIGFib3ZlIGNlcnRhaW4gdGhyZXNob2xkICh1c2VmdWwgdG8gZXh0cmFjdCB2YWx1ZSdzICJ0cmVuZCIpIApjKAogIGxlbmd0aChFQ09FWFBfVFJBTlNGJHdlaWdodFsod2hpY2goRUNPRVhQX1RSQU5TRiR3ZWlnaHQgPiA0NSkpXSksCiAgbGVuZ3RoKEVDT0VYUF9UUkFOU0Ykd2VpZ2h0Wyh3aGljaChFQ09FWFBfVFJBTlNGJHdlaWdodCA+IDEwMCkpXSksCiAgbGVuZ3RoKEVDT0VYUF9UUkFOU0Ykd2VpZ2h0Wyh3aGljaChFQ09FWFBfVFJBTlNGJHdlaWdodCA+IDIwMCkpXSksCiAgbGVuZ3RoKEVDT0VYUF9UUkFOU0Ykd2VpZ2h0Wyh3aGljaChFQ09FWFBfVFJBTlNGJHdlaWdodCA+IDMwMCkpXSksCiAgbGVuZ3RoKEVDT0VYUF9UUkFOU0Ykd2VpZ2h0Wyh3aGljaChFQ09FWFBfVFJBTlNGJHdlaWdodCA+IDQwMCkpXSksCiAgbGVuZ3RoKEVDT0VYUF9UUkFOU0Ykd2VpZ2h0Wyh3aGljaChFQ09FWFBfVFJBTlNGJHdlaWdodCA+IDUwMCkpXSksCiAgbGVuZ3RoKEVDT0VYUF9UUkFOU0Ykd2VpZ2h0Wyh3aGljaChFQ09FWFBfVFJBTlNGJHdlaWdodCA+IDYwMCkpXSksCiAgbGVuZ3RoKEVDT0VYUF9UUkFOU0Ykd2VpZ2h0Wyh3aGljaChFQ09FWFBfVFJBTlNGJHdlaWdodCA+IDcwMCkpXSksCiAgbGVuZ3RoKEVDT0VYUF9UUkFOU0Ykd2VpZ2h0Wyh3aGljaChFQ09FWFBfVFJBTlNGJHdlaWdodCA+IDgwMCkpXSksCiAgbGVuZ3RoKEVDT0VYUF9UUkFOU0Ykd2VpZ2h0Wyh3aGljaChFQ09FWFBfVFJBTlNGJHdlaWdodCA+IDkwMCkpXSkKKQojIE1heCBjb25maWRlbmNlIHZhbHVlCm1heChFQ09FWFBfVFJBTlNGJHdlaWdodCkgCmBgYApBbmQgdGhlbiwgYXMgcHJldmlvdXNseSBkb25lLCB3ZSBtYWtlIGEgYmFycGxvdCBhbmQgYSBoaXN0b2dyYW0gdG8gaGVscCB1cy4KYGBge3J9CmJhcnBsb3QoRUNPRVhQX1RSQU5TRiR3ZWlnaHQsIHlsYWI9IkNvbmZpZGVuY2UiLCB5bGltPWMoMCwxMDAwKSkKaGlzdChFQ09FWFBfVFJBTlNGJHdlaWdodCwgeGxhYj0iQ29uZmlkZW5jZSIsIHhsaW09YygwLDEwMDApLCB5bGltPWMoMCw2MDAwMCksIG1haW49Ikhpc3RvZ3JhbSBvZiBjb25maWRlbmNlIHZhbHVlcyIsIGNvbCA9IGMocmVwKCJncmF5IiwgOSksIHJlcCgicmVkIiwgMTEpKSkKYGBgCldoaWNoIGlzIHRoZSB2YWx1ZSBmb3IgYW4gYXBwcm9wcmlhdGUgdGhyZXNob2xkPwpgYGB7cn0KdGFpbCh0YWJsZShFQ09FWFBfVFJBTlNGJHdlaWdodCksIG49MzAwKQpgYGAKSSdsbCBrZWVwIHZhbHVlcyA+IDQ0OCAoYnV0IEkgcmV0YWluIGtlZXBpbmcgdmFsdWVzIOKJpSA3OTIgd291bGRuJ3QgYmUgYW4gZXJyb3Igc2luY2Ugd2UgaGF2ZSB2ZXJ5IGxpdHRsZSB2YWx1ZXMgYmV0d2VlbiA0NDggYW5kIDc5Miwgc2ltaWxhcmx5IHRvIHRoZSBwcmV2aW91cyBhbmFseXNpcyB3aGVuIHdlIHRhbGtlZCBhYm91dCB0aGUgNDA2LTc2MiAiZ2FwIikuCmBgYHtyfQptaW5FeHAgPC0gNDQ4CiMgUmVtb3ZlIGNvbmZpZGVuY2UgdmFsdWVzID4gNDQ4IHdoaWxlIGtlZXBpbmcgb25seSBjb2x1bW5zIG9mIGludGVyZXN0IGZvciB0aGlzIHByb2plY3QKRUNPRVhQX1RSQU5TRiA8LSBhcy5kYXRhLmZyYW1lKGNiaW5kKEVDT1BQSVRBQkxFJHByb3RlaW4xW0VDT1BQSVRBQkxFJGV4cGVyaW1lbnRzX3RyYW5zZmVycmVkPm1pbkV4cF0sIEVDT1BQSVRBQkxFJHByb3RlaW4yW0VDT1BQSVRBQkxFJGV4cGVyaW1lbnRzX3RyYW5zZmVycmVkPm1pbkV4cF0sIEVDT1BQSVRBQkxFJGV4cGVyaW1lbnRzX3RyYW5zZmVycmVkW0VDT1BQSVRBQkxFJGV4cGVyaW1lbnRzX3RyYW5zZmVycmVkPm1pbkV4cF0pKQpjb2xuYW1lcyhFQ09FWFBfVFJBTlNGKSA8LSBjKCJwMSIsICJwMiIsICJ3ZWlnaHQiKQpFQ09FWFBfVFJBTlNGJHdlaWdodCA8LSBhcy5udW1lcmljKEVDT0VYUF9UUkFOU0Ykd2VpZ2h0KQpgYGAKRm9yIHRoZSBtb21lbnQsIGxldCdzIGZvY3VzIG9uIHRoZSBuZXR3b3JrIHdpdGggZXhwZXJpbWVudGFsbHkgdmVyaWZpZWQgaW50ZXJhY3Rpb25zIG9ubHkuCmBgYHtyfQpsaWJyYXJ5KGlncmFwaCkKR0VYUCA8LSBncmFwaC5kYXRhLmZyYW1lKEVDT0VYUCkKYGBgCkFuZCBleHRyYWN0IGNvbm5lY3RlZCBjb21wb25lbnRzLCBhbHNvIGhhdmluZyBhIGxvb2sgYXQgdGhlIGdyYXBoIHVzaW5nIFtDeXRvc2NhcGVdKGh0dHBzOi8vY3l0b3NjYXBlLm9yZy8pLgpgYGB7cn0KQ0MgPC0gY29tcG9uZW50cyhHRVhQKQojIEhvdyBtYW55PwpsZW5ndGgodW5pcXVlKENDJG1lbWJlcnNoaXApKQp3ZSA8LSBnZXQuZWRnZS5hdHRyaWJ1dGUoR0VYUCwid2VpZ2h0IikKbGlicmFyeShSQ3kzKQpsaWJyYXJ5KHBseXIpCmN5dG9zY2FwZVBpbmcoKSAjIE5vdGUgdGhhdCBDeXRvc2NhcGUgbXVzdCBiZSBpbnN0YWxsZWQgYW5kIHJ1biB0byBwaW5nIGl0CmNyZWF0ZU5ldHdvcmtGcm9tSWdyYXBoKEdFWFAsbmV3LnRpdGxlPSdHRVhQJykKYGBgCgohW10oLi8xLnBkZil7d2lkdGg9MTAwJSBoZWlnaHQ9NDUwfQoKQWx0ZXJuYXRpdmVseSwgd2UgY2FuIGRvIHRoZSBhYm92ZSBmb3IgKkVDT0VYUF9UUkFOU0YqLCBnZXR0aW5nIGEgbXVjaCBkZW5zZXIgKG1vcmUgY29ubmVjdGVkKSBuZXR3b3JrLiBXZSdsbCB1c2UgdGhlIGxhc3QgbWVudGlvbmVkIG5ldHdvcmsgZm9yIGZ1cnRoZXIgYW5hbHlzaXMuCmBgYHtyfQpHRVhQX1RSQU5TRiA8LSBncmFwaC5kYXRhLmZyYW1lKEVDT0VYUF9UUkFOU0YpCkNDVCA8LSBjb21wb25lbnRzKEdFWFBfVFJBTlNGKQojIEhvdyBtYW55PwpsZW5ndGgodW5pcXVlKENDVCRtZW1iZXJzaGlwKSkKd2VUIDwtIGdldC5lZGdlLmF0dHJpYnV0ZShHRVhQX1RSQU5TRiwid2VpZ2h0IikKIyBUbyBmaWx0ZXIgaXQgKHdlIHdvbid0IG5lZWQgdG8gZG8gaXQgYmVjYXVzZSB3ZSBoYXZlIGFscmVhZHkgZmlsdGVyZWQgbG93IGNvbmZpZGVuY2UgdmFsdWVzKToKIyBHRVhQX1RSQU5TRiA8LSBkZWxldGVfZWRnZXMoR0VYUF9UUkFOU0YsIGVkZ2VzID0gd2hpY2god2VUIDwgbWluRXhwKSkKIyBDQ1QgPC0gY29tcG9uZW50cyhHRVhQX1RSQU5TRikgCmNyZWF0ZU5ldHdvcmtGcm9tSWdyYXBoKEdFWFBfVFJBTlNGLG5ldy50aXRsZT0nR0VYUF9UUkFOU0YnKQpgYGAKCiFbXSguLzIucGRmKXt3aWR0aD0xMDAlIGhlaWdodD00NTB9CgpUbyBpZGVudGlmeSBub24tYm90dGxlbmVjayBodWJzIGFuZCBib3R0bGVuZWNrIGh1YnMgd2UgY2FsY3VsYXRlIHRoZSBiZXR3ZWVubmVzcyBjZW50cmFsaXR5IChoaWdoIHZhbHVlID09IGJvdHRsZW5lY2spLgpgYGB7cn0KQkVUIDwtIGJldHdlZW5uZXNzKEdFWFBfVFJBTlNGLCBkaXJlY3RlZD1GQUxTRSkKaGlzdChCRVQpCmBgYApBcyB0aGV5IGRpZCBpbiBwYXBlcnMsIHdlIHNpbXBseSBkZWZpbmUgYm90dGxlbmVja3Mgb24gdGhlIGJhc2lzIG9mIHRoZSBxdWFudGlsZS4KYGBge3J9CiMgMC45IHRocmVzaG9sZCB0byBjb25zaWRlciBhIHByb3RlaW4gYSBib3R0bGVuZWNrIGluIHRoZSBQUEkKQkVUX1RBVSA8LSBxdWFudGlsZShCRVQsIDAuOSkKR0VYUF9UUkFOU0ZfREVHIDwtIGlncmFwaDo6ZGVncmVlKEdFWFBfVFJBTlNGKQpgYGAKVGhlbiB3ZSBkZWZpbmUgSFVCUy4KYGBge3J9CiMgMC45IHRocmVzaG9sZCB0byBjb25zaWRlciBhIHByb3RlaW4gYSBodWIgaW4gdGhlIFBQSQpERUdfVEFVIDwtIHF1YW50aWxlKEdFWFBfVFJBTlNGX0RFRywgMC45KQpgYGAKVG8gc2VsZWN0IGJvdHRsZW5lY2staHVicyAoQkgpIHdlIGNvbWJpbmUgdGhlIHRocmVzaG9sZHMgdG8gaWRlbnRpZnkgYSBzZXQgb2YgcHJvdGVpbnMgZnVsZmlsbGluZyBib3RoLgpgYGB7cn0KQkggPC0gVihHRVhQX1RSQU5TRikkbmFtZVtCRVQ+PUJFVF9UQVUgJiBHRVhQX1RSQU5TRl9ERUc+PURFR19UQVVdCmBgYApTZWxlY3Qgbm9uLWJvdHRsZW5lY2sgaHVicyAoTkJIKToKYGBge3J9Ck5CSCA8LSBWKEdFWFBfVFJBTlNGKSRuYW1lW0JFVDxCRVRfVEFVICYgR0VYUF9UUkFOU0ZfREVHPj1ERUdfVEFVXQpgYGAKU2VsZWN0IGJvdHRsZW5lY2sgbm9uLWh1YiAoQk5IKToKYGBge3J9CkJOSCA8LSBWKEdFWFBfVFJBTlNGKSRuYW1lW0JFVD49QkVUX1RBVSAmIEdFWFBfVFJBTlNGX0RFRzxERUdfVEFVXQpgYGAKU2VsZWN0IG5vbi1ib3R0bGVuZWNrIG5vbiBodWJzIE5CTkg6CmBgYHtyfQpOQk5IIDwtIFYoR0VYUF9UUkFOU0YpJG5hbWVbQkVUPEJFVF9UQVUgJiBHRVhQX1RSQU5TRl9ERUc8REVHX1RBVV0KYGBgCkNoZWNrIHRoZSBsZW5ndGggb2YgdGhlIHZlY3RvcnM6CmBgYHtyfQpjKAogIGxlbmd0aChCSCksIGxlbmd0aChOQkgpLCBsZW5ndGgoQk5IKSwgbGVuZ3RoKE5CTkgpCikKYGBgCk5vbi1ib3R0bGVuZWNrIG5vbi1odWJzIChOQk5IKSBhcmUgdGhlIGxhcmdlc3QgZ3JvdXAgYXMgc2FpZCBieSB0aGUgdmVyeSBiZWdpbm5pbmcuIAoKSW4gdGhpcyBwcm9qZWN0LCBpbnN0ZWFkIG9mIGhhdmluZyB0byBkbyB3aXRoIGEgbGlzdCBvZiBlc3NlbnRpYWwgZ2VuZXMgc3VjaCB0aGF0IHlvdSBjYW4gc2ltcGx5IGNhbGN1bGF0ZSB0aGUgbGVuZ3RoIG9mIHRoZSBpbnRlcnNlY3Rpb24sIHdlIG5lZWQgdG8gY2FsY3VsYXRlIGEgY29ycmVsYXRpb24gY29lZmZpY2llbnQgZGlzdHJpYnV0aW9uIGZvciBhbGwgdGhlIGludGVyYWN0b3JzIG9mIHRoZSBwcm90ZWlucyB3aXRoaW4gZWFjaCBvZiB0aGUgQkggYW5kIE5CSCAoaW4gdGhpcyBwcm9qZWN0IHdlIGZvY3VzIG9uIGh1YnMpLiBUaGVyZWZvcmUsIGF0IHRoaXMgcG9pbnQgd2UgZG93bmxvYWQgZ2VuZSBleHByZXNzaW9uIGxldmVscyBmcm9tIGEgcHJvdmlkZWQgW2dlbmUgZXhwcmVzc2lvbiBjb21wZW5kaXVtIGZvciBFLiBjb2xpXShodHRwczovL2dpdGh1Yi5jb20vZW1hbnVlbGVjYXZhbGxlcmkvcGFydHlBTkRkYXRlUFBuZXR3b3Jrcy9ibG9iL21haW4vRWNvbGlfY29tcGVuZGl1bS5SRGF0YT9yYXc9dHJ1ZSksIHRoZW4gd2UnbGwgbWF0Y2ggcHJvdGVpbiBuYW1lcyBoZXJlIGluIHRoZSBncmFwaCBhbmQgaW4gdGhlIGNvbXBlbmRpdW0uCmBgYHtyfQpsb2FkKHVybCgiaHR0cHM6Ly9naXRodWIuY29tL2VtYW51ZWxlY2F2YWxsZXJpL3BhcnR5QU5EZGF0ZVBQbmV0d29ya3MvYmxvYi9tYWluL0Vjb2xpX2NvbXBlbmRpdW0uUkRhdGE/cmF3PXRydWUiKSkgIyBGcm9tIG5vdyBvbiB3ZSdsbCBmb2N1cyBvbiBaTDIgbWF0cml4LCBidXQgd2UgaGF2ZSAzIGRpZmZlcmVudCB2ZXJzaW9ucyBvZiBhIGdlbmUgZXhwcmVzc2lvbiBjb21wZW5kaXVtIGZvciBFLiBjb2xpICh3ZSBjb3VsZCd2ZSB1c2VkIEFMTF9GSU5BTF9OcmVhZHMgYW5kIEFMTF9GSU5BTF9UUE0gdG9vKS4gTW9yZW92ZXIsIHdlIGRvbuKAmXQgY2FyZSBvbiBjb25kaXRpb25zIChjb2x1bW5zIG9mIHRoZSBtYXRyaWNlcykgaW4gdGhpcyBwcm9qZWN0CmBgYApGb3IgaW5zdGFuY2UsIHRvIGhhdmUgYW4gaWRlYSBhYm91dCB0aGUgY29udGVudCBvZiB0aGUgbWF0cml4LCBoZXJlIHdlIHJlcG9ydCB0aGUgNiBmaXJzdCBjb25kaXRpb25zJyBnZW5lIGV4cHJlc3Npb24gbGV2ZWxzIGZvciB0aGUgZW50cnkgKmIzMDY1Kiwgd2hpY2ggaXMgYSBzbWFsbCBzdWJ1bml0IHJpYm9zb21hbCBwcm90ZWluIGJlbG9uZ2luZyB0byB0aGUgYmFjdGVyaWFsIHJpYm9zb21hbCBwcm90ZWluIGJTMjEgZmFtaWx5IGFsc28ga25vd24gYXMgInJwc1UiLgpgYGB7cn0KaGVhZChaTDJbImIzMDY1IixdKQpgYGAKRm9yIHRoZSBzYWtlIG9mIGNvbXBsZXRlbmVzcyBoZXJlIHdlIGFkZCBbU1RSSU5HXShodHRwczovL3N0cmluZy1kYi5vcmcvY2dpL25ldHdvcms/dGFza0lkPWJTbXNYZVVWaXZsMSZzZXNzaW9uSWQ9YjdyNW9tQVdPMFFRKSBhbmQgW0FscGhhRm9sZF0oaHR0cHM6Ly9hbHBoYWZvbGQuZWJpLmFjLnVrL2VudHJ5L0EwQTEzM04zTTcpIGVudHJpZXMgcmVsYXRpdmUgdG8gYjMwNjUvcnBzVS4KCiFbW1NUUklOR10oaHR0cHM6Ly9zdHJpbmctZGIub3JnL2NnaS9uZXR3b3JrP3Rhc2tJZD1iU21zWGVVVml2bDEmc2Vzc2lvbklkPWI3cjVvbUFXTzBRUSldKHN0cmluZ19oaXJlc19pbWFnZS5wbmcpCgohW1tBbHBoYUZvbGRdKGh0dHBzOi8vYWxwaGFmb2xkLmViaS5hYy51ay9lbnRyeS9BMEExMzNOM003KV0oYWxwaGEucG5nKQoKV2UgYXJlIGludGVyZXN0ZWQgaW4gaG93IG11Y2ggdGhlIGludGVyYWN0b3JzIGFyZSBjb3JyZWxhdGVkOyBiZWZvcmUgdGhlIGxvb3AgdG8gY2FsY3VsYXRlIHRoZSBwZWFyc29ucyBmb3IgZWFjaCBtZW1iZXIgb2YgdGhlIGJvdHRsZW5lY2sgaHViIChCSCkgZ3JvdXAsIHdlIGRlZmluZToKYGBge3J9CmZpeGVkX2ludGVydmFscyA8LSBzZXEoLTEuMDUsIDEuMDUsIC4xKQphbGxwZWFyc29uc0JIIDwtIG1hdHJpeCgwLCBuY29sPWxlbmd0aChmaXhlZF9pbnRlcnZhbHMpLTEsIG5yb3c9bGVuZ3RoKEJIKSkKYGBgCk5vdyBpdGVyYXRlIG92ZXIgdGhlIEJIOgpgYGB7cn0KZm9yKGkgaW4gMTpsZW5ndGgoQkgpKXsKICAjIE5laWdoYm9yIGdlbmVzIHN1Y2ggdGhhdCB3ZSBjYW4gZXh0cmFjdCB0aGUgY29ycmVzcG9uZGluZyByb3dzCiAgIyBmcm9tIHRoZSBjb21wZW5kaXVtIAogIGh1Yl9pbnRlcmFjdG9ycyA8LSBuZWlnaGJvcmhvb2QoR0VYUF9UUkFOU0Ysbm9kZXM9QkhbaV0pW1sxXV0KICAjIEJIW2ldIGlzIHRoZSBmaXJzdCBpbiB0aGUgaHViX2ludGVyYWN0b3JzIGxpc3Qgb2YgaW50ZXJhY3RvcnMKICAjIGV4dHJhY3RlZCB3aXRoIG5laWdoYm9yaG9vZCwgdGhlcmVmb3JlIGl0IGlzIGluIGZpcnN0IHJvdzsKICAjIHdlIGFyZSBpbnRlcmVzdGVkIGluIHRoZSBjb3JyZWxhdGlvbiBvZiBpbnRlcmFjdG9ycyBvZiBCSFtpXQogICMgd2hlbiBCSFtpXSBpcyAicHJlc2VudCIsIHRoZXJlZm9yZToKICBtYXRjaCA8LSBtYXRjaChuYW1lcyhodWJfaW50ZXJhY3RvcnMpLCByb3duYW1lcyhaTDIpKQogIGV4dHJhY3RlZHJvd3MgPC0gWkwyW21hdGNoLF0KICBiaGlfZXhwcmVzc2VkIDwtIHdoaWNoKGV4dHJhY3RlZHJvd3NbMSxdID4gbWVhbihleHRyYWN0ZWRyb3dzWzEsXSkpCiAgZXh0cmFjdGVkcm93cyA8LSBleHRyYWN0ZWRyb3dzWyxiaGlfZXhwcmVzc2VkXQogICMgUmVtb3ZlIHRoZSBodWIgdW5kZXIgYW5hbHlzaXM6CiAgZXh0cmFjdGVkcm93cyA8LSBleHRyYWN0ZWRyb3dzWy0xLF0KICAjIENhbGN1bGF0ZSBhIG1hdHJpeCBvZiBjb3JyZWxhdGlvbiBvZiBhbGwgcHJvdGVpbnMgaW50ZXJhY3RpbmcKICAjIHdpdGggQkhbaV0gdnMgdGhlbXNlbHZlcyBpbiBhbGwgY29tYmluYXRpb25zOgogIHBlYXJzb25zIDwtIGNvcih0KGV4dHJhY3RlZHJvd3MpKQogICMgQWRkaXRpb25hbGx5IHdlIHRyZWF0IGFsbCBvZiB0aGVtIGF0IG9uY2UsIHdlIHRyYW5zZm9ybSBpdCBpbnRvIGEgdmVjdG9yCiAgcGVhcnNvbnMgPC0gcGVhcnNvbnNbdXBwZXIudHJpKHBlYXJzb25zKV0KICAjIE5vdyB3ZSBzdG9yZSB0aGUgZGlzdHJpYnV0aW9uIG9mIHBlYXJzb25zIGZvciBlYWNoIG1lbWJlciAKICAjIG9mIHRoZSBjYXRlZ29yeSAoaGVyZSBCSCkgYW5kIHRoZW4gcHV0IHRoZW0gdG9nZXRoZXI7CiAgIyB0aGlzIGNhbiBiZSBkb25lIGJ5IHB1dHRpbmcgYWxsIHBlYXJzb24gdmFsdWVzIHRvZ2V0aGVyIGZvciBhbGwgbWVtYmVycwogICMgb2YgYSBncm91cCBhbmQgdGhlbiBwbG90IGhpc3RvZ3JhbXMuCiAgIyBJbiBkb2luZyBzbyBob3dldmVyLCBodWJzIHdpdGggbGFyZ2VyIG51bWJlciBvZiBpbnRlcmFjdG9ycyB3aWxsIGdldCBtb3JlIHdlaWdodC4KICBwYXIobWZyb3c9YygxLDIpKSAjIFNldHRpbmcgdGhlIHBsb3R0aW5nIGFyZWEgaW50byBhIDEqMiBhcnJheQogIGlmKGkgPj0xICYmIGkgPD0gMyl7CiAgICAjIFdlJ2xsIHNob3cgeW91IG9ubHkgdGhlIGZpcnN0IDMgcGxvdHMgZm9yIGVhc2Ugb2YgdmlzdWFsaXphdGlvbi4KICAgICMgVG8gZ2l2ZSB0aGUgc2FtZSB3ZWlnaHQgdG8gdGhlIGRpc3RyaWJ1dGlvbiBvZiBwZWFyc29ucyB2YWx1ZSBmb3IgZGlmZmVyZW50IGh1YnM6CiAgICBoIDwtIGhpc3QocGVhcnNvbnMsIGJyZWFrcz1maXhlZF9pbnRlcnZhbHMpCiAgICAjIGgkY291bnRzL3N1bShoJGNvdW50cykgYW5kIGgkbWlkcyBpbmRpY2F0ZXMgdGhlIG1pZGRsZSBwb2ludCBvZiBlYWNoIGludGVydmFsLgogICAgIyBUaGVuLCB3aXRoaW4gdGhlIGxvb3AsIHdlIGNhbGN1bGF0ZSB0aGUgYWJvdmUgaGlzdG9ncmFtCiAgICBwbG90KGgkbWlkcywgaCRjb3VudHMvc3VtKGgkY291bnRzKSkKICB9CiAgZWxzZQogICAgaCA8LSBoaXN0KHBlYXJzb25zLCBicmVha3M9Zml4ZWRfaW50ZXJ2YWxzLCBwbG90PUZBTFNFKQogICMgYW5kIHN0b3JlIHBlYXJzb25zIGNvcnJlbGF0aW9uIGNvZWZmaWNpZW50czoKICBhbGxwZWFyc29uc0JIW2ksXSA8LSBoJGNvdW50cy9zdW0oaCRjb3VudHMpCn0KYGBgCkF0IHRoZSBlbmQgd2UgaGF2ZSAqYWxscGVhcnNvbnNCSCogcG9wdWxhdGVkIGJ5IHRoZSB2YWx1ZXMgb2YgYWxsIG1lbWJlcnMgb2YgdGhlIGdyb3VwLiBBcmUgYWxsIEJIIHNpbWlsYXIvZGlmZmVyZW50IGZvciB0aGUgd2F5IHRoZWlyIHRhcmdldHMgYXJlIGNvcnJlbGF0ZWQ/CmBgYHtyfQpsaWJyYXJ5KHBoZWF0bWFwKQpjb2xuYW1lcyhhbGxwZWFyc29uc0JIKSA8LSBoJG1pZHMKcGhlYXRtYXAoYWxscGVhcnNvbnNCSCwgY2x1c3Rlcl9jb2xzID0gRkFMU0UpCmBgYApGb3IgcGFydHkgaHVicyBtb3N0IGludGVyYWN0b3JzIHNob3VsZCBiZSBjb2V4cHJlc3NlZCBhdCB0aGUgc2FtZSB0aW1lLDogd2Ugbm90aWNlIGEgcGVhayBvbiB0aGUgcmlnaHQgKHRoZSBwcmV2aW91cyBjaG9pY2Ugb2YgYSBoaWdoIHRocmVzaG9sZCBoaWdobGlnaHRzIHRoaXMgZmFjdCkuXApDb252ZXJzZWx5LCBkYXRlIGh1YnMgaW50ZXJhY3Qgd2l0aCBkaWZmZXJlbnQgaW50ZXJhY3RvcnMgYXQgZGlmZmVyZW50IHRpbWVzLCB0aGVyZWZvcmUgdGhlIGRpc3RyaWJ1dGlvbiBvZiBjb3JyZWxhdGlvbnMgYW1vbmcgdGhlIGludGVyYWN0b3JzIHNob3VsZCBiZSBtb3JlIGZsYXQgb3Igd2l0aCBzZXZlcmFsIHBlYWtzLgpgYGB7cn0KYWxscGVhcnNvbnNOQkggPC0gbWF0cml4KDAsIG5jb2w9bGVuZ3RoKGZpeGVkX2ludGVydmFscyktMSwgbnJvdz1sZW5ndGgoTkJIKSkKZm9yKGkgaW4gMTpsZW5ndGgoTkJIKSl7CiAgaHViX2ludGVyYWN0b3JzIDwtIG5laWdoYm9yaG9vZChHRVhQX1RSQU5TRixub2Rlcz1OQkhbaV0pW1sxXV0KICBtYXRjaCA8LSBtYXRjaChuYW1lcyhodWJfaW50ZXJhY3RvcnMpLCByb3duYW1lcyhaTDIpKQogIGV4dHJhY3RlZHJvd3MgPC0gWkwyW21hdGNoLF0KICBuYmhpX2V4cHJlc3NlZCA8LSB3aGljaChleHRyYWN0ZWRyb3dzWzEsXSA+IG1lYW4oZXh0cmFjdGVkcm93c1sxLF0pKQogIGV4dHJhY3RlZHJvd3MgPC0gZXh0cmFjdGVkcm93c1ssbmJoaV9leHByZXNzZWRdCiAgZXh0cmFjdGVkcm93cyA8LSBleHRyYWN0ZWRyb3dzWy0xLF0KICBwZWFyc29ucyA8LSBjb3IodChleHRyYWN0ZWRyb3dzKSkKICBwZWFyc29ucyA8LSBwZWFyc29uc1t1cHBlci50cmkocGVhcnNvbnMpXQogIHBhcihtZnJvdz1jKDEsMikpCiAgaWYoaSA+PSAxICYmIGkgPD0gMyl7CiAgICBoIDwtIGhpc3QocGVhcnNvbnMsIGJyZWFrcz1maXhlZF9pbnRlcnZhbHMpCiAgICBwbG90KGgkbWlkcywgaCRjb3VudHMvc3VtKGgkY291bnRzKSkKICB9CiAgZWxzZQogICAgaCA8LSBoaXN0KHBlYXJzb25zLCBicmVha3M9Zml4ZWRfaW50ZXJ2YWxzLCBwbG90PUZBTFNFKQogIGFsbHBlYXJzb25zTkJIW2ksXSA8LSBoJGNvdW50cy9zdW0oaCRjb3VudHMpCn0KY29sbmFtZXMoYWxscGVhcnNvbnNOQkgpIDwtIGgkbWlkcwpwaGVhdG1hcChhbGxwZWFyc29uc05CSCwgY2x1c3Rlcl9jb2xzID0gRkFMU0UpCmBgYApXZSBjYW4gY29uZmlybSBvdXIgaHlwb3RoZXNpczogd2UgY2FuIGlkZW50aWZ5IHBlYWtzIGluIHRoZSBoZWF0bWFwIGFuZCBhIG1vcmUgInNjYXR0ZXJlZCIgcGxvdC4gVGhlIGZhY3QgdGhhdCBzZXZlcmFsIHBlYWtzIGFyZSBwcmVzZW50IG1heSBiZSBhbiBpbmRpY2F0aW9uIG9mIG11bHRpcGxlIGNvbXBsZXhlcyBmb3JtZWQgYnkgdGhlIGh1YiBhbmQgYWxzbyBvZiBtYW55IGRpZmZlcmVudCBpbnRlcmFjdGlvbnMgd2l0aCBvbmUgb3IgYSBmZXcgcGFydG5lcnMgYXQgYSB0aW1lLgoKTGV0J3Mgbm93IHNlZSBpZiB0aGVyZSBpcyBzb21lIGlubmVyIHN0cnVjdHVyZSBieSB1c2luZyBzb21lIGRpbWVuc2lvbmFsaXR5IHJlZHVjdGlvbiB0ZWNobmlxdWUgc3VjaCBhcyBbVU1BUF0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvTm9ubGluZWFyX2RpbWVuc2lvbmFsaXR5X3JlZHVjdGlvbiNVbmlmb3JtX21hbmlmb2xkX2FwcHJveGltYXRpb25fYW5kX3Byb2plY3Rpb24pIGFuZCBbUENBXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9QcmluY2lwYWxfY29tcG9uZW50X2FuYWx5c2lzKSBvbiBCSCBhbmQgTkJIIGdyb3Vwczogd2UnbGwgZm9jdXMgb24gVU1BUCBmaXJzdC4KYGBge3J9CmxpYnJhcnkodXdvdCkKVUJIIDwtIHVtYXAoYWxscGVhcnNvbnNCSCwgbl9jb21wb25lbnRzID0gMywgbl9uZWlnaGJvcnMgPSAyKQpsaWJyYXJ5KHJnbCkKb3B0aW9ucyhyZ2wudXNlTlVMTCA9IFRSVUUpCnBsb3QzZCh4PVVCSFssMV0sIHk9VUJIWywyXSwgej1VQkhbLDNdLCBjb2w9InJlZCIpCnJnbHdpZGdldCgpClVOQkggPC0gdW1hcChhbGxwZWFyc29uc05CSCwgbl9jb21wb25lbnRzID0gMykKcGxvdDNkKHg9VU5CSFssMV0sIHk9VU5CSFssMl0sIHo9VU5CSFssM10sIGNvbD0iZ3JlZW4iKQpyZ2x3aWRnZXQoKQpgYGAKQW5kIHdoYXQgaWYgd2Ugam9pbiB0aGUgKmFsbHBlYXJzb25zQkgqIGFuZCAqYWxscGVhcnNvbnNOQkgqIGFuZCBkbyB0aGUgc2FtZT8gV2UgY2FuIGFsc28gY29sb3IgQkggYW5kIE5CSCBpdGVtcyBkaWZmZXJlbnRseSB0byBzZWUgaWYgdGhleSBncm91cCB0b2dldGhlci4KYGBge3J9CmFsbHBlYXJzb25zIDwtIHJiaW5kKGFsbHBlYXJzb25zQkgsIGFsbHBlYXJzb25zTkJIKQpVIDwtIHVtYXAoYWxscGVhcnNvbnMsIG5fY29tcG9uZW50cyA9IDMpCnBsb3QzZCh4PVVbLDFdLCB5PVVbLDJdLCB6PVVbLDNdLCBjb2wgPSBjKHJlcCgicmVkIiwgbGVuZ3RoKEJIKSksIHJlcCgiZ3JlZW4iLCBsZW5ndGgoTkJIKSkpKQpsZWdlbmQzZCgidG9wcmlnaHQiLCBsZWdlbmQgPSBjKCdCSCcsICdOQkgnKSwgcGNoID0gMTYsIGNvbCA9IGMoInJlZCIsICJncmVlbiIpLCBjZXg9MSwgaW5zZXQ9YygwLjAyKSkKcmdsd2lkZ2V0KCkKYGBgCldlIGNhbiByZWNvZ25pemUgdGhyZWUgZ3JvdXBzIG9mIGludGVyZXN0IGluIHRoZSBzcGFjZS4gRm9jdXNpbmcgb24gQkggcG9pbnRzIHdlIGNhbiBub3RpY2UgdGhhdCB0aGUgMTMgcG9pbnRzIGJlbG9uZ2luZyB0byB0aGF0IGNhdGVnb3J5IGFyZSBub3QgYWxsIGdyb3VwZWQgdG9nZXRoZXIsIGJ1dCB3ZSBjYW4gc2VlIHRoYXQgdGhlIG1ham9yaXR5IGFyZSB3aXRoaW4gYSBwcmVjaXNlIHJlZ2lvbiBvZiB0aGUgM0Qgc3BhY2UgYW5kIHRoYXQgdGhlIHRocmVlL2ZvdXIgb3V0c2lkZSB0aGUgImNvbmRlbnNlZCIgcmVnaW9uIHNlZW0gdG8gZm9sbG93IGEgcHJlY2lzZSB0cmFqZWN0b3J5IHRvd2FyZCB0aGUgb3RoZXIgdHdvIE5CSCBncm91cHMsIHNvIG1heWJlIGEgc2lnbiBvZiBzaW1pbGFyIGJlaGF2aW9yLiBJbiBvdGhlciB3b3JkcywgQkggcG9pbnRzIGFyZSBub3QgaXNvbGF0ZWQgaW4gdGhlIHNwYWNlLCBidXQgd2l0aGluIHJlZ2lvbnMgYWxzbyBwb3B1bGF0ZWQgYnkgTkJIIHBvaW50cyAoYXQgbGVhc3Qgb25lIEJIIHBvaW50IGluIGFueSBOQkggcmVnaW9uKS4KCk5vdyBsZXQncyBmb2N1cyBvbnRvIFBDQSB0ZWNobmlxdWUuCmBgYHtyfQpQQ0EgPC0gcHJjb21wKGFsbHBlYXJzb25zKQpzY29yZXMgPSBhcy5kYXRhLmZyYW1lKFBDQSR4KQpsaWJyYXJ5KGdncGxvdDIpCmdncGxvdChkYXRhID0gc2NvcmVzLCBhZXMoeCA9IFBDMSwgeSA9IFBDMiwgbGFiZWwgPSByb3duYW1lcyhzY29yZXMpKSkgKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAsIGNvbG91ciA9ICJncmF5NjUiKSArCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMCwgY29sb3VyID0gImdyYXk2NSIpICsKICBnZW9tX3RleHQoY29sb3VyID0gYyhyZXAoInJlZCIsIGxlbmd0aChCSCkpLCByZXAoImdyZWVuIiwgbGVuZ3RoKE5CSCkpKSwgYWxwaGEgPSAwLjgsIHNpemUgPSA0KQpgYGAKUENBIG1ha2VzIGl0IGV2ZW4gY2xlYXJlciBkZWZpbml0ZWx5IGNvbmZpcm1pbmcgb3VyIHJlc3VsdHMgcHJldmlvdXNseSBvYnRhaW5lZDogcmVkIHBvaW50cyAoY29taW5nIGZyb20gdGhlIEJIIGdyb3VwKSwgZXhjZXB0IGZvciBwb2ludHMgMiBhbmQgMyBhbmQgaWYgeW91IHdhbnQgMTMgKHNpbWlsYXJseSB0byB0aGUgMy80IHBvaW50cyBkaXN0YW50IGZyb20gdGhlIG1vc3QgcG9wdWxhdGVkIGdyb3VwIGluIFVNQVAgM0QgcGxvdCksIHRlbmQgdG8gZ3JvdXAgdG9nZXRoZXIsIHdoaWxlIG90aGVyIG9uZXMgKGJlbG9uZ2luZyB0byB0aGUgTkJIIGdyb3VwKSB0ZW5kIHRvIGJlIHNwYXJzZXIgKGN1cmlvdXNseSBzaW1pbGFyIHRvIHRoZSBmYWN0IHRoYXQgcHJldmlvdXNseSB3ZSBvYnRhaW5lZCBhIG1vcmUgInNjYXR0ZXJlZCIgaGVhdG1hcCkuCgojIFNlc3Npb24gaW5mb3M6CmBgYHtyfQpjeXRvc2NhcGVWZXJzaW9uSW5mbygpCnNlc3Npb25JbmZvKCkKYGBg